﻿// -------------------------------------------------------------------------
//    @FileName         :    NFMagicTimeUtil.cpp
//    @Author           :    Gao.Yi
//    @Date             :   2022-09-18
//    @Email			:    445267987@qq.com
//    @Module           :    NFMagicTimeUtil.cpp
//
// -------------------------------------------------------------------------

#include "NFTimeUtility.h"

#include "NFServerTime.h"
#include "NFSnprintf.h"
#include "NFTime.h"

// 判断是否为闰年
#define IS_LEAP_YEAR(year) \
    ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
//  每个月最后一天距离1月1日的天数
static const int YearDayOffset[2][13] = {{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}};
// 本算法适用于 2013-01-01 00:00:00 ~ 2038年1月19日03:14:07 之间的time_t的转化
// 起始时间2013-01-01 00:00:00的time_t
#define START_SEC   1356969600
#define DAY_SECONDS 86400
#define NONLEAP_YEAR_DAYS  365

int NFTimeUtility::m_iTimeAbsDayOffSet = GetAbsDayOffSet();
int NFTimeUtility::m_iTimeAbsWeekOffSet = GetAbsWeekOffSet();
int NFTimeUtility::m_iCurTimeZone = CountCurTimeZone();

#ifdef I18N_GLOBAL
int NFTimeUtility::m_iStaticTimeOffset = 0;

int NFTimeUtility::GetTimeZoneOffset()
{
    return m_iStaticTimeOffset;
}

void NFTimeUtility::SetTimeZoneOffset(int iTimeOffset)
{
    m_iStaticTimeOffset = iTimeOffset;
}
#endif

int NFTimeUtility::GetAbsDayOffSet()
{
    time_t tTime = 0;
    tm* ptm = localtime(&tTime);
    return ptm->tm_hour * 60 * 60;
}

//monday is a new week. not sunday
int NFTimeUtility::GetAbsWeekOffSet()
{
    time_t tTime = 0;
    tm* ptm = localtime(&tTime);
    return (ptm->tm_wday - 1) * 60 * 60 * 24 + ptm->tm_hour * 60 * 60; //这里减1 是因为: 周四就是值4, 如果按周一为每周的起点的话, 要减1.
}

//获取当前服务器所在的时区
int32_t NFTimeUtility::GetCurTimeZone()
{
    return m_iCurTimeZone;
}

//获取当前服务器所在的时区
int32_t NFTimeUtility::CountCurTimeZone()
{
    time_t utcSec = NFGetSecondTime();
    tm curData;
    localtime_r(&utcSec, &curData);

    tm *pUtcData = gmtime(&utcSec);

    int32_t tmpHour = curData.tm_hour - pUtcData->tm_hour;

    if (tmpHour < -12)
    {
        tmpHour += 24;
    }
    else if (tmpHour > 12)
    {
        tmpHour -= 24;
    }

    return tmpHour;
}

/**
 * @brief 将时间（秒）转换为时:分:秒格式的std::string
 * @param myTime 时间值（单位：秒）
 * @return 格式化的时间字符串（HH:MM:SS格式）
 * @note 线程安全，推荐使用此版本替代CSecondToStr
 */
std::string NFTimeUtility::SecondToStr(time_t myTime)
{
    return NFSprintfFunc("%02d:%02d:%02d", (int)myTime / 3600, (int)(myTime % 3600) / 60, (int)(myTime % 3600) % 60);
}

std::string NFTimeUtility::DateTimeToStrSimCn(time_t tMyTime)
{
#ifdef I18N_GLOBAL
    tMyTime += LOCAL_TIME_CORRECTION;
#endif
    tm curr;
    localtime_r(&tMyTime, &curr);

    if (curr.tm_year > 50)
    {
        return NFSprintfFunc("%04d年%02d月%02d日 ", curr.tm_year + 1900, curr.tm_mon + 1, curr.tm_mday);
    }
    else
    {
        return NFSprintfFunc("%04d年%02d月%02d日 ", curr.tm_year + 2000, curr.tm_mon + 1, curr.tm_mday);
    }
}

std::string NFTimeUtility::SecondTimeToStr(const NFTimeVal& tvTime)
{
    return NFSprintfFunc("%ld:%ld", tvTime.tv_sec, tvTime.tv_usec);
}

std::string NFTimeUtility::DateTimeToStr(time_t myTime, bool bOnlyDay)
{
#ifdef I18N_GLOBAL
    myTime += LOCAL_TIME_CORRECTION;
#endif
    struct tm curr;
    localtime_r(&myTime, &curr);

    if (bOnlyDay)
    {
        return NFSprintfFunc("%02d:%02d:%02d", curr.tm_hour, curr.tm_min, curr.tm_sec);
    }
    else if (curr.tm_year > 50)
    {
        return NFSprintfFunc("%04d-%02d-%02d %02d:%02d:%02d", curr.tm_year + 1900, curr.tm_mon + 1, curr.tm_mday, curr.tm_hour, curr.tm_min, curr.tm_sec);
    }
    else
    {
        return NFSprintfFunc("%04d-%02d-%02d %02d:%02d:%02d", curr.tm_year + 2000, curr.tm_mon + 1, curr.tm_mday, curr.tm_hour, curr.tm_min, curr.tm_sec);
    }
}

int NFTimeUtility::DataStrToLocalTimeNew(const char* pStr, time_t* pTime)
{
    if (!pStr || !pTime)
    {
        return -1;
    }

    tm lmt = {};

    int n = sscanf(pStr, "%04d-%02d-%02d %02d:%02d:%02d", &lmt.tm_year, &lmt.tm_mon, &lmt.tm_mday, &lmt.tm_hour, &lmt.tm_min, &lmt.tm_sec);

    if (n < 3 || n > 6)
    {
        return -1;
    }

    lmt.tm_year -= 1900;
    lmt.tm_mon -= 1;
    lmt.tm_isdst = 0;
    *pTime = mktime(&lmt);
#ifdef I18N_GLOBAL
    *pTime -= LOCAL_TIME_CORRECTION;
#endif
    return 0;
}

int NFTimeUtility::DateStrToLocalTime(const char* pStr, time_t* pTime)
{
    if (!pStr || !pTime)
    {
        return -1;
    }

    tm lmt;

    int n = sscanf(pStr, "%04d-%02d-%02d %02d:%02d:%02d", &lmt.tm_year, &lmt.tm_mon, &lmt.tm_mday, &lmt.tm_hour, &lmt.tm_min, &lmt.tm_sec);

    if (3 != n && 6 != n)
    {
        return -1;
    }

    if (n == 3)
    {
        lmt.tm_hour = 0;
        lmt.tm_min = 0;
        lmt.tm_sec = 0;
    }

    if (lmt.tm_mon < 1 || lmt.tm_mon > 12)
    {
        return -1;
    }
    if (lmt.tm_mday < 1 || lmt.tm_mday > 31)
    {
        return -1;
    }
    if (lmt.tm_hour < 0 || lmt.tm_hour > 23)
    {
        return -1;
    }
    if (lmt.tm_min < 0 || lmt.tm_min > 59)
    {
        return -1;
    }
    if (lmt.tm_sec < 0 || lmt.tm_sec > 59)
    {
        return -1;
    }

    lmt.tm_year -= 1900;
    lmt.tm_mon -= 1;
    lmt.tm_isdst = 0;
    *pTime = mktime(&lmt);
#ifdef I18N_GLOBAL
    *pTime -= LOCAL_TIME_CORRECTION;
#endif
    return 0;
}

int NFTimeUtility::GetTimeSecEx(const char* pStr, time_t* pTime)
{
    if (!pStr || !pTime)
    {
        return -1;
    }

    int iHour = 0;
    int iMin = 0;

    int n = sscanf(pStr, "%02d:%02d", &iHour, &iMin);

    if (n < 1 || iHour < 0 || iHour > 23 || iMin < 0 || iMin > 59)
    {
        return -1;
    }

    *pTime = 3600 * iHour + 60 * iMin;

    return 0;
}

int NFTimeUtility::GetTimeSec(const char* pStr, time_t* pTime)
{
    if (!pStr || !pTime)
    {
        return -1;
    }

    int iHour = 0;
    int iMin = 0;
    int iSec = 0;

    int n = sscanf(pStr, "%02d:%02d:%02d", &iHour, &iMin, &iSec);

    if (n < 1)
    {
        return -1;
    }

    *pTime = 3600 * iHour + 60 * iMin + iSec;

    return 0;
}

long NFTimeUtility::MakeTime(const char* szTime)
{
    char sTemp[20], s1[5];
    tm tmTime;

    if (strlen(szTime) != 19)
    {
        return 0;
    }

    NFSafeSnprintf(sTemp, sizeof(sTemp), "%s", szTime);

    NFSafeSnprintf(s1, sizeof(s1), "%s", &sTemp[17]);
    sTemp[16] = 0;
    tmTime.tm_sec = atoi(s1);

    NFSafeSnprintf(s1, sizeof(s1), "%s", &sTemp[14]);
    sTemp[13] = 0;
    tmTime.tm_min = atoi(s1);

    NFSafeSnprintf(s1, sizeof(s1), "%s", &sTemp[11]);
    sTemp[10] = 0;
    tmTime.tm_hour = atoi(s1);

    NFSafeSnprintf(s1, sizeof(s1), "%s", &sTemp[8]);
    sTemp[7] = 0;
    tmTime.tm_mday = atoi(s1);

    NFSafeSnprintf(s1, sizeof(s1), "%s", &sTemp[5]);
    sTemp[4] = 0;
    tmTime.tm_mon = atoi(s1) - 1;

    tmTime.tm_year = atoi(sTemp) - 1900;

    tmTime.tm_isdst = 0; //标准时间，非夏令时
    time_t lTime = mktime(&tmTime);

#ifdef I18N_GLOBAL
    lTime -= LOCAL_TIME_CORRECTION;
#endif
    return lTime;
}

time_t NFTimeUtility::MakeTime(int iYear, int iMonth, int iDay, int iHour, int iMinute, int iSecond)
{
    tm tmTime;

    tmTime.tm_year = iYear - 1900;
    tmTime.tm_mon = iMonth - 1;
    tmTime.tm_mday = iDay;
    tmTime.tm_hour = iHour;
    tmTime.tm_min = iMinute;
    tmTime.tm_sec = iSecond;

    tmTime.tm_isdst = 0; // 标准时间，非夏令时

#ifdef I18N_GLOBAL
    return mktime(&tmTime) - LOCAL_TIME_CORRECTION;
#else
    return mktime(&tmTime);
#endif
}

time_t NFTimeUtility::StrToTime(const char* pTime)
{
    time_t tTime = 0;

    int iRetCode = StrToTime(pTime, &tTime);

    if (iRetCode < 0)
    {
        return 0;
    }

    return tTime;
}


int NFTimeUtility::StrToTime(const char* pTime, time_t* ptime)
{
    if (!pTime || !ptime)
    {
        return -1;
    }

    tm lmt;

    int n = sscanf(pTime, "%04d-%02d-%02d %02d:%02d:%02d", &lmt.tm_year, &lmt.tm_mon, &lmt.tm_mday, &lmt.tm_hour, &lmt.tm_min, &lmt.tm_sec);

    if (6 != n)
    {
        return -1;
    }

    if (lmt.tm_mon < 1 || lmt.tm_mon > 12)
    {
        return -1;
    }
    if (lmt.tm_mday < 1 || lmt.tm_mday > 31)
    {
        return -1;
    }
    if (lmt.tm_hour < 0 || lmt.tm_hour > 23)
    {
        return -1;
    }
    if (lmt.tm_min < 0 || lmt.tm_min > 59)
    {
        return -1;
    }
    if (lmt.tm_sec < 0 || lmt.tm_sec > 59)
    {
        return -1;
    }

    lmt.tm_year -= 1900;
    lmt.tm_mon -= 1;
    lmt.tm_isdst = 0;
    *ptime = mktime(&lmt);
#ifdef I18N_GLOBAL
    *ptime -= LOCAL_TIME_CORRECTION;
#endif
    return 0;
}

/**
 * @brief 根据Unix时间戳获取对应的年月日信息
 * @param iCurTime Unix时间戳（秒）
 * @param iYear [输出参数] 年份
 * @param iMonth [输出参数] 月份（1-12）
 * @param iMonthDay [输出参数] 月份中的天数（1-31）
 * @return 0 成功；-1 失败（月份计算异常）
 * @note 本算法适用于2013-01-01 00:00:00 ~ 2038年1月19日03:14:07之间的时间转换
 * @note 在I18N_GLOBAL宏定义下会自动加上本地时区偏移量
 * @note 从2013年开始计算以减少循环次数，提高性能
 * @warning 输入时间超出支持范围可能导致计算错误
 */
int NFTimeUtility::GetCurDate(unsigned int iCurTime, int& iYear, int& iMonth, int& iMonthDay)
{
#ifdef I18N_GLOBAL
    iCurTime += LOCAL_TIME_CORRECTION;
#endif
    //
    int iTime = iCurTime - START_SEC;
    int iDay = iTime / DAY_SECONDS;
    // 从2013年开始计算，为了减少while的循环数量
    int iStartYear = 2013;
    int iStartDay = 0;

    do
    {
        iStartDay += NONLEAP_YEAR_DAYS;

        if (IS_LEAP_YEAR(iStartYear))
        {
            ++iStartDay;
        }

        iStartYear++;
    }
    while (iStartDay <= iDay);

    --iStartYear;
    iStartDay -= NONLEAP_YEAR_DAYS;
    int iIsLeapYear = IS_LEAP_YEAR(iStartYear);

    if (iIsLeapYear)
    {
        --iStartDay;
    }

    int iYearDay = iDay - iStartDay;
    int iMon = 0;

    while (iMon < (int)sizeof(YearDayOffset[0]) && YearDayOffset[iIsLeapYear][iMon] <= iYearDay)
    {
        ++iMon;
    }

    iYear = iStartYear;
    iMonth = iMon;

    if (iMon <= 0)
    {
        return -1;
    }

    iMonthDay = iYearDay - YearDayOffset[iIsLeapYear][iMon - 1] + 1;
    return 0;
}

/**
 * @brief 根据Unix时间戳获取对应的星期几和时分秒信息
 * @param iCurTime Unix时间戳（秒）
 * @param week [输出参数] 星期几（1-7，1为星期一，7为星期日）
 * @param hour [输出参数] 小时（0-23）
 * @param minute [输出参数] 分钟（0-59）
 * @param second [输出参数] 秒（0-59）
 * @return 0 成功
 * @note 在I18N_GLOBAL宏定义下会自动加上本地时区偏移量
 * @note 先计算星期几，再减去当天开始时间得到当天内的秒数
 * @note 用于游戏中需要按星期和具体时间进行逻辑判断的场景
 */
int NFTimeUtility::GetCurTime(unsigned int iCurTime, int& week, int& hour, int& minute, int& second)
{
#ifdef I18N_GLOBAL
    iCurTime += LOCAL_TIME_CORRECTION;
#endif
    week = GetWeekDay127(iCurTime);
    iCurTime -= GetTodayStartTime(iCurTime);

    hour = iCurTime / 3600;
    iCurTime -= 3600 * hour;
    minute = iCurTime / 60;
    iCurTime -= 60 * minute;
    second = iCurTime;

    return 0;
}

int NFTimeUtility::GetCurHour(time_t iCurTime)
{
#ifdef I18N_GLOBAL
    iCurTime += LOCAL_TIME_CORRECTION;
#endif
    tm* ptm = localtime(&iCurTime);
    return ptm->tm_hour;
}

int NFTimeUtility::GetCurMin(time_t iCurTime)
{
#ifdef I18N_GLOBAL
    iCurTime += LOCAL_TIME_CORRECTION;
#endif
    tm* ptm = localtime(&iCurTime);
    return ptm->tm_min;
}

bool NFTimeUtility::IsSameHour(time_t tCur, time_t tBefore)
{
    if (tCur == tBefore)
    {
        return true;
    }

    time_t tAHours = tCur / SECONDS_AHOUR;
    time_t tBHours = tBefore / SECONDS_AHOUR;

    if (tAHours == tBHours)
    {
        return true;
    }

    return false;
}

bool NFTimeUtility::IsSameDayStd(time_t tCur, time_t tBefore, int iHour)
{
    if (tCur > tBefore)
    {
        return GetDayElapse(tCur, tBefore, iHour) == 0;
    }
    else
    {
        return GetDayElapse(tBefore, tCur, iHour) == 0;
    }
}

bool NFTimeUtility::IsSameWeekStd(time_t tNow, time_t tBeforeTime)
{
    return GetWeekStartTime(tNow, 0) == GetWeekStartTime(tBeforeTime, 0);
}


bool NFTimeUtility::IsSameDay(time_t tCur, time_t tBefore)
{
    if (tCur > tBefore)
    {
        return GetDayElapse(tCur, tBefore, GAME_RESET_HOUR_EVERYDAY) == 0;
    }
    else
    {
        return GetDayElapse(tBefore, tCur, GAME_RESET_HOUR_EVERYDAY) == 0;
    }
}

bool NFTimeUtility::IsSameDayCommon(time_t tCur, time_t tBefore)
{
    return IsSameDayCommonNoCase(tCur, tBefore);
}

bool NFTimeUtility::IsSameDayCommonNoCase(time_t tCur, time_t tBefore)
{
    if (tCur > tBefore)
    {
        return GetDayElapse(tCur, tBefore) == 0;
    }
    else
    {
        return GetDayElapse(tBefore, tCur) == 0;
    }
}

time_t NFTimeUtility::GetTodayStartTimeByMin(time_t tTimeNow, int iHour, int iMin)
{
    int iMinSecond = iHour * 3600 + 60 * iMin;
    time_t tNowLocal = tTimeNow + LOCAL_TIME_CORRECTION - iMinSecond;
    time_t tNowDays = tNowLocal / SECONDS_ADAY;
    time_t tNowDayStart = tNowDays * SECONDS_ADAY;
    tNowDayStart = tNowDayStart - LOCAL_TIME_CORRECTION + iMinSecond;
    return tNowDayStart;
}

bool NFTimeUtility::IsSameMonth(time_t tCur, time_t tBefore)
{
    return IsSameMonthStd(tCur, tBefore, 0);
}

bool NFTimeUtility::IsSameYearStd(time_t tCur, time_t tBefore, int iHour)
{
    time_t tCurStart = GetYearStartTime(tCur, iHour);
    time_t tBeforeStart = GetYearStartTime(tBefore, iHour);

    return (tCurStart == tBeforeStart);
}

bool NFTimeUtility::IsSameYear(time_t tCur, time_t tBefore)
{
    return IsSameYearStd(tCur, tBefore, 0);
}

uint16_t NFTimeUtility::GetYearDay(time_t tTime)
{
#ifdef I18N_GLOBAL
    tTime += LOCAL_TIME_CORRECTION;
#endif
    tm* ptm = localtime(&tTime);
    if (ptm == nullptr)
    {
        return 0;
    }

    return ptm->tm_yday;
}

time_t NFTimeUtility::GetYearStartTime(time_t tTime, int iHour)
{
#ifdef I18N_GLOBAL
    tTime += LOCAL_TIME_CORRECTION;
#endif
    int iHourSecond = iHour * 3600;
    time_t tHourTime = tTime - iHourSecond;

    tm EndTm;
    if (localtime_r(&tHourTime, &EndTm) == nullptr)
    {
        return 0;
    }

    EndTm.tm_hour = 0;
    EndTm.tm_min = 0;
    EndTm.tm_sec = 0;
    EndTm.tm_mday = 1;
    EndTm.tm_mon = 0;
    time_t tEndTime = mktime(&EndTm);
#ifdef I18N_GLOBAL
    tEndTime -= LOCAL_TIME_CORRECTION;
#endif
    return tEndTime;
}

time_t NFTimeUtility::GetYearEndTime(time_t tTime, int iHour)
{
#ifdef I18N_GLOBAL
    tTime += LOCAL_TIME_CORRECTION;
#endif
    int iHourSecond = iHour * 3600;
    time_t tHourTime = tTime - iHourSecond;

    tm EndTm;
    if (localtime_r(&tHourTime, &EndTm) == nullptr)
    {
        return 0;
    }

    EndTm.tm_hour = 0;
    EndTm.tm_min = 0;
    EndTm.tm_sec = 0;
    EndTm.tm_mday = 1;
    EndTm.tm_mon = 0;
    EndTm.tm_year = EndTm.tm_year + 1;
    time_t tEndTime = mktime(&EndTm);
#ifdef I18N_GLOBAL
    tEndTime -= LOCAL_TIME_CORRECTION;
#endif
    return tEndTime;
}

int NFTimeUtility::GetWeekDay(time_t tTime)
{
#ifdef I18N_GLOBAL
    tTime += LOCAL_TIME_CORRECTION;
#endif
    tm* ptm = localtime(&tTime);
    return ptm->tm_wday;
}

int NFTimeUtility::GetWeekDay127(time_t tTime)
{
    time_t iLocalTime = tTime + LOCAL_TIME_CORRECTION;
    time_t iWeekDay = ((iLocalTime / SECONDS_ADAY + 3) % 7) + 1;
    return iWeekDay;
}

int NFTimeUtility::GetMonthDay(time_t tTime)
{
#ifdef I18N_GLOBAL
    tTime += LOCAL_TIME_CORRECTION;
#endif
    tm* ptm = localtime(&tTime);
    return ptm->tm_mday;
}

/**
 * @brief 获取指定时间的月份天数（带时区偏移）
 * @param tTime 输入的时间戳（GMT时间）
 * @param iTimeOffset 时区偏移量（秒），用于调整到本地时间
 * @return 返回该时间所在月份的第几天（1-31）
 * @note 该函数通过添加时区偏移来调整时间，然后获取本地时间的月份天数
 * @note 与NFTimeUtility::GetMonthDay功能类似，但支持自定义时区偏移
 * @warning 调用方需确保iTimeOffset参数的正确性，错误的偏移可能导致日期计算错误
 */
int NFTimeUtility::GetMonthDay(time_t tTime, int iTimeOffset)
{
    tTime += iTimeOffset;
    tm* ptm = localtime(&tTime);
    return ptm->tm_mday;
}

int NFTimeUtility::GetOffsetWeeks(time_t tTime, time_t tNow)
{
    if (tTime >= tNow)
    {
        return 0;
    }

    time_t tTimeWeekStart = GetWeekStartTime(tTime);
    time_t tNowWeekStart = GetWeekStartTime(tNow);

    if (tTimeWeekStart >= tNowWeekStart)
    {
        return 0;
    }

    return (tNowWeekStart - tTimeWeekStart) / SECONDS_AWEEK;
}

/**
 * @brief 根据指定星期几和时间偏移计算本周内绝对时间戳
 * @param tNow 当前时间戳（GMT时间）
 * @param bWeekDay 目标星期几（1-7，1为星期一，7为星期日）
 * @param dwWeekTime 一周内的时间偏移（秒），从周一0点开始计算
 * @return 返回本周内指定星期几和时间的绝对时间戳
 * @note 计算基于本周的开始时间（周一0点）进行偏移
 * @note 如果bWeekDay超出1-7范围，计算结果可能不准确
 * @note GetAbsTimeByWeekDayAndTime(now, 3, 3600*10) 返回本周三上午10点的时间戳
 */
uint32_t NFTimeUtility::GetAbsTimeByWeekDayAndTime(time_t tNow, uint8_t bWeekDay, uint32_t dwWeekTime)
{
    uint32_t dwThisWeekStartTime = GetWeekStartTime(tNow);

    return dwThisWeekStartTime + (bWeekDay-1) * SECONDS_ADAY + dwWeekTime;
}

time_t NFTimeUtility::GetWeekStartTime(time_t tTime, int iHour)
{
    int iHourSecond = iHour * 3600;
    time_t iLocalTime = tTime + LOCAL_TIME_CORRECTION - iHourSecond;
    time_t iLocalDayStartTime = (iLocalTime / SECONDS_ADAY) * SECONDS_ADAY;
    int iWeekDay = GetWeekDay127(tTime - iHourSecond);
    time_t iLocalThisWeekStartTime1 = iLocalDayStartTime - (iWeekDay - 1) * SECONDS_ADAY;
    return iLocalThisWeekStartTime1 - LOCAL_TIME_CORRECTION + iHourSecond; //to GMT
}

time_t NFTimeUtility::GetWeekEndTime(time_t tTime, int iHour)
{
    int iHourSecond = iHour * 3600;
    time_t iLocalTime = tTime + LOCAL_TIME_CORRECTION - iHourSecond;
    time_t iLocalDayStartTime = (iLocalTime / SECONDS_ADAY) * SECONDS_ADAY;
    int iWeekDay = GetWeekDay127(tTime - iHourSecond);
    time_t iLocalThisWeekEndTime = iLocalDayStartTime + (7 - (iWeekDay - 1)) * SECONDS_ADAY;
    return iLocalThisWeekEndTime - LOCAL_TIME_CORRECTION + iHourSecond;
}

bool NFTimeUtility::IsSameWeek127(time_t tNow, time_t tBeforeTime, int iHour)
{
    //时间往回调整的情况
    if (tNow <= tBeforeTime)
    {
        return true;
    }

    return GetWeekStartTime(tNow, iHour) == GetWeekStartTime(tBeforeTime, iHour);
}

int NFTimeUtility::GetWeekCycleChainIndex(time_t tNow, int iChainLength)
{
    time_t tWeekStartTime = GetWeekStartTime(tNow, GAME_RESET_HOUR_EVERYDAY);
    //逻辑上只要保证算法一致就行，不要求非常硬性的规则
    int iIndex = (tWeekStartTime / SECONDS_AWEEK) % iChainLength;
    //1,2,3...iChainLength
    return iIndex + 1;
}

uint32_t NFTimeUtility::GetCurDaySec(uint32_t dwUnixSec, int iHour)
{
    return dwUnixSec - GetTodayStartTime(dwUnixSec, iHour);
}

uint32_t NFTimeUtility::GetCurDay(uint32_t dwUnixSec, int iHour)
{
    return GetDayElapse(dwUnixSec, 0, iHour);
}

time_t NFTimeUtility::GetTodayStartTimeWithOffset(time_t tTimeNow, int iOffset)
{
    int iMinSecond = iOffset;
    time_t tNowLocal = tTimeNow + LOCAL_TIME_CORRECTION - iMinSecond;
    time_t tNowDays = tNowLocal / SECONDS_ADAY;
    time_t tNowDayStart = tNowDays * SECONDS_ADAY;
    tNowDayStart = tNowDayStart - LOCAL_TIME_CORRECTION + iMinSecond;
    return tNowDayStart;
}

time_t NFTimeUtility::GetWeekStartTimeWithOffset(time_t tTime, int iOffset)
{
    int iHourSecond = iOffset;
    time_t iLocalTime = tTime + LOCAL_TIME_CORRECTION - iHourSecond;
    time_t iLocalDayStartTime = (iLocalTime / SECONDS_ADAY) * SECONDS_ADAY;
    int iWeekDay = GetWeekDay127(tTime - iHourSecond);
    time_t iLocalThisWeekStartTime1 = iLocalDayStartTime - (iWeekDay - 1) * SECONDS_ADAY;
    return iLocalThisWeekStartTime1 - LOCAL_TIME_CORRECTION + iHourSecond; //to GMT
}

time_t NFTimeUtility::GetWeekDayWithOffset(time_t tTime, int iOffset)
{
    return (tTime - GetWeekStartTimeWithOffset(tTime, iOffset)) / SECONDS_ADAY + 1;
}

/**
 * @brief 获取本月开始和结束时间
 * @param tTime 输入的时间戳
 * @param tBegin 输出参数，本月开始时间
 * @param tEnd 输出参数，本月结束时间
 * @param iHour 偏移小时数，默认从指定小时开始计算
 * @note 该函数会计算指定时间所在月份的开始和结束时间，支持时区偏移
 */
void NFTimeUtility::GetMonthStartEndTime(time_t tTime, time_t& tBegin, time_t& tEnd, int iHour)
{
#ifdef I18N_GLOBAL
    tTime += LOCAL_TIME_CORRECTION;
#endif
    int iHourSecond = iHour * 3600;
    time_t tHourTime = tTime - iHourSecond;
    tm nowtm = *localtime(&tHourTime);
    nowtm.tm_hour = 0;
    nowtm.tm_min = 0;
    nowtm.tm_sec = 0;
    nowtm.tm_mday = 1;
    time_t tStartTime = mktime(&nowtm);
    int iMonAddDay = 30;

    if (nowtm.tm_mon == 1) //2月
    {
        iMonAddDay = 28;
    }

    time_t tEndTime = tStartTime + (iMonAddDay * SECONDS_ADAY);
    tm MonthEndTm = *localtime(&tEndTime);

    if (MonthEndTm.tm_mon == nowtm.tm_mon)
    {
        tEndTime += SECONDS_ADAY;
    }

    tBegin = tStartTime + iHourSecond;
    tEnd = tEndTime + iHourSecond;
#ifdef I18N_GLOBAL
    tBegin -= LOCAL_TIME_CORRECTION;
    tEnd -= LOCAL_TIME_CORRECTION;
#endif
}

/**
 * @brief 获取本月开始时间
 * @param tTime 输入的时间戳
 * @param iHour 偏移小时数，从指定小时开始计算月份
 * @return 返回本月开始时间的时间戳，失败返回0
 * @note 计算指定时间所在月份的第一天指定小时的时间戳
 */
time_t NFTimeUtility::GetMonthStartTime(time_t tTime, int iHour)
{
#ifdef I18N_GLOBAL
    tTime += LOCAL_TIME_CORRECTION;
#endif
    int iHourSecond = iHour * 3600;
    time_t tHourTime = tTime - iHourSecond;
    tm* ptm = localtime(&tHourTime);
    if (!ptm)
    {
        return 0;
    }

    tm nowtm = *ptm;
    nowtm.tm_hour = 0;
    nowtm.tm_min = 0;
    nowtm.tm_sec = 0;
    nowtm.tm_mday = 1;
    time_t tStartTime = mktime(&nowtm);
#ifdef I18N_GLOBAL
    tStartTime -= LOCAL_TIME_CORRECTION;
#endif
    return tStartTime + iHourSecond;
}

/**
 * @brief 获取本月结束时间
 * @param tTime 输入的时间戳
 * @param iHour 偏移小时数，从指定小时开始计算月份
 * @return 返回本月结束时间的时间戳，失败返回0
 * @note 计算指定时间所在月份的下个月第一天指定小时的时间戳
 * @warning 对于2月份只考虑了28天的情况，未处理闰年29天的case
 */
time_t NFTimeUtility::GetMonthEndTime(time_t tTime, int iHour)
{
#ifdef I18N_GLOBAL
    tTime += LOCAL_TIME_CORRECTION;
#endif
    int iHourSecond = iHour * 3600;
    time_t tHourTime = tTime - iHourSecond;
    tm* ptm = localtime(&tHourTime);
    if (!ptm)
    {
        return 0;
    }

    tm nowTm = *ptm;
    nowTm.tm_hour = 0;
    nowTm.tm_min = 0;
    nowTm.tm_sec = 0;
    nowTm.tm_mday = 1;
    time_t tStartTime = mktime(&nowTm);
    int iMonAddDay = 30;

    if (nowTm.tm_mon == 1) //2月
    {
        iMonAddDay = 28;
    }

    time_t tEndTime = tStartTime + (iMonAddDay * SECONDS_ADAY);

    tm MonthEndtm;
    if (localtime_r(&tEndTime, &MonthEndtm) == nullptr)
    {
        return 0;
    }

    if (MonthEndtm.tm_mon == nowTm.tm_mon)
    {
        tEndTime += SECONDS_ADAY;
    }
#ifdef I18N_GLOBAL
    tEndTime -= LOCAL_TIME_CORRECTION;
#endif
    return tEndTime + iHourSecond;
}

/**
 * @brief 判断两个时间戳是否在同一个月（标准版本）
 * @param tNow 当前时间戳（GMT时间）
 * @param tBeforeTime 之前的时间戳（GMT时间）
 * @param iHour 偏移小时数，用于调整月份计算的起始时间点
 * @return true 如果两个时间在同一个月，false 否则
 * @note 通过比较两个时间各自所在月份的开始时间来判断是否为同月
 * @note 支持时区偏移和自定义月份起始小时
 */
bool NFTimeUtility::IsSameMonthStd(time_t tNow, time_t tBeforeTime, int iHour)
{
    time_t tNowDel = GetMonthStartTime(tNow, iHour);
    time_t tBeforeDel = GetMonthStartTime(tBeforeTime, iHour);
    return tNowDel == tBeforeDel;
}

/*
这个函数确定是正确的
tNow ,tTime是GMT（UTC）时间,+8后才是本地时间
*/
bool NFTimeUtility::IsAfterDayByUtcTime(time_t tNow, time_t tTime, int iHour)
{
    if (tNow <= tTime)
    {
        return false;
    }

    int iHourSecond = iHour * 3600;
    //UTC转成本地时间
    tNow += LOCAL_TIME_CORRECTION - iHourSecond;
    tTime += LOCAL_TIME_CORRECTION - iHourSecond;
    time_t tNowDays = tNow / SECONDS_ADAY;
    time_t tTimeDays = tTime / SECONDS_ADAY;

    if (tNowDays > tTimeDays)
    {
        return true;
    }

    return false;
}

int NFTimeUtility::GetDayElapse(time_t tNow, time_t tTime, int iHour)
{
    if (tNow <= tTime)
    {
        return 0;
    }

    int iHourSecond = iHour * 3600;
    //UTC转成本地时间
    time_t tNowLocal = tNow + LOCAL_TIME_CORRECTION - iHourSecond;
    time_t tTimeLocal = tTime + LOCAL_TIME_CORRECTION - iHourSecond;
    time_t tNowDays = tNowLocal / SECONDS_ADAY;
    time_t tTimeDays = tTimeLocal / SECONDS_ADAY;
    return tNowDays - tTimeDays;
}

uint32_t NFTimeUtility::GetAbsDay(time_t tNow, int iHour)
{
    return (tNow + LOCAL_TIME_CORRECTION - iHour * SECONDS_AHOUR) / SECONDS_ADAY;
}

time_t NFTimeUtility::GetTodayStartTime(time_t tTimeNow, int iHour)
{
    int iHourSecond = iHour * 3600;
    //UTC转成本地时间
    time_t tNowLocal = tTimeNow + LOCAL_TIME_CORRECTION - iHourSecond;
    time_t tNowDays = tNowLocal / SECONDS_ADAY;
    time_t tNowDayStart = tNowDays * SECONDS_ADAY;
    tNowDayStart = tNowDayStart - LOCAL_TIME_CORRECTION + iHourSecond;
    return tNowDayStart;
}

time_t NFTimeUtility::GetTodayEndTime(time_t tTimeNow, int iHour)
{
    time_t tStartTime = GetTodayStartTime(tTimeNow, 0);

    return tStartTime + SECONDS_ADAY;
}

std::string NFTimeUtility::GetWeekDayStringByStartZero(uint8_t bWeekDayIndex)
{
#if NF_PLATFORM != NF_PLATFORM_WIN
    switch (bWeekDayIndex)
    {
        case 0:
            return "星期一";

        case 1:
            return "星期二";

        case 2:
            return "星期三";

        case 3:
            return "星期四";

        case 4:
            return "星期五";

        case 5:
            return "星期六";

        case 6:
            return "星期日";
    }
#endif
    return "--";
}

bool NFTimeUtility::IsTimePassedS(time_t tNow, time_t tLast, int iGap)
{
    return (tNow - tLast) >= iGap;
}

bool NFTimeUtility::IsTimePassedMs(timeval tvNow, timeval tvLast, int iGapMs)
{
    int64_t i64RealGapMS = (tvNow.tv_sec - tvLast.tv_sec) * 1000 + (tvNow.tv_usec - tvLast.tv_usec) / 1000;
    return i64RealGapMS >= iGapMs;
}

int NFTimeUtility::GetWeekDayIndexWithStartHour(uint32_t dwTimeNow, uint8_t bStartHour)
{
    return GetWeekDay127(dwTimeNow - bStartHour * 3600);
}

bool NFTimeUtility::IsOkWithWeekDayCtrl(uint8_t bWeekDayCtrlFlag, uint32_t dwTimeNow)
{
    int iWeekDay = GetWeekDayIndexWithStartHour(dwTimeNow, GAME_RESET_HOUR_EVERYDAY);
    uint8_t bFlag = (0x1 << (iWeekDay - 1));

    if (BIT_ENABLED(bWeekDayCtrlFlag, bFlag))
    {
        return true;
    }

    return false;
}

time_t NFTimeUtility::ToTimestamp(int iYear, int iMon, int iMDay/*day of the month */, int iHour, int iMin, int iSec)
{
    tm timeInfo = {};

    timeInfo.tm_year = iYear - 1900;
    timeInfo.tm_mon = iMon - 1;

    timeInfo.tm_mday = iMDay;

    timeInfo.tm_hour = iHour;
    timeInfo.tm_min = iMin;
    timeInfo.tm_sec = iSec;

    time_t tTimestamp = mktime(&timeInfo);
    return tTimestamp;
    /*
    tm_wday  和 tm_yday  不需要设置，mktime会回填他们正确的值
	 from http://stackoverflow.com/questions/9575131/why-is-mktime-changing-the-year-day-of-my-tm-struct
    The original values of the tm_wday and tm_yday components of the structure are ignored, and the original values of the other components are not restricted to the ranges indicated above. On successful completion, the values of the tm_wday and tm_yday components of the structure are set appropriately,

    */
}

short NFTimeUtility::GetCurMonthDay(int year, int month)
{
    switch (month)
    {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
        {
            return 31;
        }
        case 4:
        case 6:
        case 9:
        case 11:
        {
            return 30;
        }
        case 2:
        {
            if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
            {
                return 29;
            }

            break;
        }
        default:
        {
            return 0;
        }
    }

    return 28;
}

int64_t NFTimeUtility::GetNextMonthDay(int year, int month, int day, int& nMonth)
{
    int64_t diffTime = 0;
    int j = 1;
    int tYear = year;

    for (int i = month; i <= 12 && j <= 12; ++i, ++j)
    {
        int tmon = i + 1;
        if (tmon > 12)
        {
            tmon = 1;
            tYear = year + 1;
        }

        int tday = GetCurMonthDay(tYear, tmon);
        if (tday >= day)
        {
            //下个月的天数比当前的日期大或者等于，那么就出循环
            nMonth = tmon;

            break;
        }
        else
        {
            diffTime += (int64_t)tday * 24 * 3600 * 1000;
        }
    }

    if (j > 12)
    {
        diffTime = 0;
    }
    else
    {
        diffTime += (int64_t)GetCurMonthDay(year, month) * 24 * 3600 * 1000;
    }

    return diffTime;
}

int NFTimeUtility::GetOffsetToNextHour(time_t tCur)
{
    int offset = 0;

    time_t tCurHour = tCur % SECONDS_AHOUR;

    offset = SECONDS_AHOUR - tCurHour;

    return offset;
}

int NFTimeUtility::GetOffsetToNextHalfHour(time_t tCur)
{
    int offset = 0;

    time_t tCurHalfHour = tCur % SECONDS_HALF_AHOUR;

    offset = SECONDS_HALF_AHOUR - tCurHalfHour;

    return offset;
}

int NFTimeUtility::GetOffsetToNextDay(time_t tCur)
{
    int offset = 0;

    time_t tCurDay = (tCur + LOCAL_TIME_CORRECTION) % SECONDS_ADAY;

    offset = SECONDS_ADAY - tCurDay;

    return offset;
}

int NFTimeUtility::GetYear(time_t tCur)
{
    tm* ptm = localtime(&tCur);
    if (ptm)
    {
        return ptm->tm_year + 1900;
    }

    return 0;
}

int NFTimeUtility::GetDaysDelta(time_t tOne, time_t tTwo)
{
    time_t tOneLocal = tOne + LOCAL_TIME_CORRECTION;
    time_t tTwoLocal = tTwo + LOCAL_TIME_CORRECTION;
    time_t tOneDays = tOneLocal / SECONDS_ADAY;
    time_t tTwoDays = tTwoLocal / SECONDS_ADAY;
    return tOneDays - tTwoDays;
}

time_t NFTimeUtility::GetThisWeekDayTime(time_t tNow, int iWeekDay)
{
    iWeekDay -= 1;
    if (iWeekDay < 0)
    {
        iWeekDay = 0;
    }
    else if (iWeekDay >= 7)
    {
        iWeekDay %= 7;
    }

    time_t tStartTime = GetWeekStartTime(tNow);

    return tStartTime + iWeekDay * 86400;
}

uint64_t NFTimeUtility::GetTimeByWeekDay(uint64_t ullWeek, int iDay)
{
    return ullWeek + (iDay - 1) * SECONDS_ADAY;
}


// 获取时差，秒数
uint32_t NFTimeUtility::GetGmtSec()
{
    return LOCAL_TIME_CORRECTION;
}

void NFTimeUtility::AddMsWithPointer(NFTimeVal* plhs, int iMs)
{
    plhs->tv_sec += (iMs / 1000);
    plhs->tv_usec += ((iMs % 1000) * 1000);
    plhs->tv_sec += (plhs->tv_usec / 1000000);
    plhs->tv_usec = plhs->tv_usec % 1000000;
}

void NFTimeUtility::AddMs(NFTimeVal& lhs, int iMs)
{
    AddMsWithPointer(&lhs, iMs);
}

void NFTimeUtility::AddUsWithPointer(NFTimeVal* plhs, int iUs)
{
    plhs->tv_usec += iUs;
    plhs->tv_sec += (plhs->tv_usec / 1000000);
    plhs->tv_usec = plhs->tv_usec % 1000000;
}

void NFTimeUtility::AddUs(NFTimeVal& lhs, int iUs)
{
    AddUsWithPointer(&lhs, iUs);
}

int NFTimeUtility::DayElapseSeconds(int iHour, int iMinute, int iSecond)
{
    int iSeconds = iHour * 3600 + iMinute * 60 + iSecond;

    return iSeconds;
}

unsigned int NFTimeUtility::TimeMsPass(timeval* pstTv1, timeval* pstTv2)
{
    int iSec = pstTv1->tv_sec - pstTv2->tv_sec;

    if (iSec < 0 || iSec > 100000)
    {
        iSec = 100000;
    }

    return iSec * 1000 + (pstTv1->tv_usec - pstTv2->tv_usec) / 1000;
}

int64_t NFTimeUtility::TimeUsPass(timeval* pstTv1, timeval* pstTv2)
{
    int iSec = pstTv1->tv_sec - pstTv2->tv_sec;

    if (iSec < 0 || iSec > 100000)
    {
        iSec = 100000;
    }

    return iSec * 1000000 + (pstTv1->tv_usec - pstTv2->tv_usec);
}

/**
 * @brief 计算两个时间戳之间相差的月数
 * @param dwTimeNow 当前时间戳（秒）
 * @param dwTimeLast 过去的时间戳（秒）
 * @return 相差的月数，如果当前时间早于过去时间则返回-1
 * @note 计算时会根据游戏重置时间进行调整，确保按游戏逻辑计算月份差异
 * @warning 当前时间早于过去时间时返回-1，调用方需要进行有效性检查
 */
int NFTimeUtility::GetElapseMonths(uint32_t dwTimeNow, uint32_t dwTimeLast)
{
#ifdef I18N_GLOBAL
    dwTimeNow += LOCAL_TIME_CORRECTION;
    dwTimeLast += LOCAL_TIME_CORRECTION;
#endif
    if (dwTimeNow < GAME_RESET_HOUR_EVERYDAY * SECONDS_AHOUR)
    {
        dwTimeNow = GAME_RESET_HOUR_EVERYDAY * SECONDS_AHOUR;
    }

    if (dwTimeLast < GAME_RESET_HOUR_EVERYDAY * SECONDS_AHOUR)
    {
        dwTimeLast = GAME_RESET_HOUR_EVERYDAY * SECONDS_AHOUR;
    }

    time_t tNow = dwTimeNow - GAME_RESET_HOUR_EVERYDAY * SECONDS_AHOUR;
    tm* pNowtm = localtime(&tNow);
    int iNowYear = pNowtm->tm_year;
    int iNowMon = pNowtm->tm_mon + 1;

    time_t tLast = dwTimeLast - GAME_RESET_HOUR_EVERYDAY * SECONDS_AHOUR;
    tm* pLasttm = localtime(&tLast);
    int iLastYear = pLasttm->tm_year;
    int iLastMon = pLasttm->tm_mon + 1;

    if (iNowYear < iLastYear)
    {
        return -1;
    }

    int iElapseMonths = 12 * (iNowYear - iLastYear) + iNowMon - iLastMon;

    return iElapseMonths;
}

/**
 * @brief 根据服务器开服时间和开服天数计算对应的开服周数和星期几
 * @param dwServerOpenTime 服务器开服时间戳（秒）
 * @param iOpenDay 开服天数（从1开始计算）
 * @param iOpenWeek [输出参数] 计算得到的开服周数（从1开始）
 * @param iWeekDay [输出参数] 计算得到的星期几（1-7，1为星期一，7为星期日）
 * @note 用于游戏活动系统中按开服周期计算活动时间
 * @note 使用向上取整计算周数，确保第一周包含开服当天
 * @note 如果开服时间是星期三，第5天对应第2周星期日
 */
void NFTimeUtility::GetOpenDayWeekInfo(uint32_t dwServerOpenTime, int iOpenDay, int& iOpenWeek, int& iWeekDay)
{
    int iServerOpenWeekDay = GetWeekDay127(dwServerOpenTime);
    iOpenWeek = ceil((float)(iOpenDay + iServerOpenWeekDay) / 7);
    iWeekDay = (iServerOpenWeekDay + iOpenDay) % 7;
    if (iWeekDay == 0)
    {
        iWeekDay = 7;
    }
}

/**
 * @brief 解析并验证时间字符串格式，转换为Unix时间戳
 * @param pszTime 输入的时间字符串，期望格式为"YYYY-MM-DD HH:MM:SS"（长度必须为19）
 * @param dwUnixSec [输出参数] 解析成功后的Unix时间戳
 * @return 0 成功；-1 时间格式错误或解析失败
 * @note 该函数会进行双重验证：首先解析字符串为时间戳，然后将时间戳反向转换为字符串进行比对
 * @note 空字符串被视为未配置，返回成功但时间戳为0
 * @warning 时间字符串长度必须严格为19个字符，否则返回错误
 * @note CalcAndCheckTime("2024-01-15 10:30:45", timestamp) 返回0且timestamp为对应Unix时间戳
 */
int NFTimeUtility::CalcAndCheckTime(const std::string& pszTime, uint32_t& dwUnixSec)
{
    dwUnixSec = 0;

    if (pszTime.size() == 0)
    {
        // 未配置
        return 0;
    }

    // 时间格式错误 长度只能是19
    if (pszTime.size() != 19)
    {
        return -1;
    }

    dwUnixSec = MakeTime(pszTime.c_str());
    std::string strTimeCheck = DateTimeToStr(dwUnixSec);
    if (strTimeCheck != pszTime)
    {
        // 时间格式错误
        dwUnixSec = 0;
        return -1;
    }

    return 0;
}

/**
 * @brief 计算指定时间距离1970-01-05（周一）的绝对周数
 * @param tTime 输入的时间戳（GMT时间）
 * @return 返回绝对周数，从0开始计算；如果时间早于1970-01-05则返回0
 * @note 以1970-01-05作为第0周的周一开始，因为1970-01-01是周四
 * @note 使用成员变量m_iTimeAbsWeekOffSet进行时间基准调整
 * @warning 如果输入时间早于基准时间（1970-01-05），会返回0表示异常
 * @details 计算公式：(调整后时间戳) / (7天的秒数) = 周数索引
 */
uint32_t NFTimeUtility::GetAbsWeek(time_t tTime) //从1970-01-05日开始计算,当前时间为第几周
{
    int iBaseTime = (int)tTime;
    //基准时间0  是 1970-01-01 08:00:00 , 目前不考虑其他时区的情况
    //1970-01-05 才是周一, 所以还要减 4天
    iBaseTime = iBaseTime + m_iTimeAbsWeekOffSet;

    if (iBaseTime <= 0) //出错了. 时间少于 1970-01-05, 溢出了. 全部返回0
    {
        return 0;
    }

    uint32_t dwWeekIndex = iBaseTime / (7 * 60 * 60 * 24);

    return dwWeekIndex;
}

/**
 * @brief 获取指定时间在当天的绝对秒数（从00:00:00开始计算）
 * @param tTime 输入的时间戳（GMT时间）
 * @return 返回当天已过去的秒数，范围为0-86399
 * @note 在I18N_GLOBAL宏定义下会自动加上本地时区偏移量
 * @note 计算公式：小时*3600 + 分钟*60 + 秒
 * @note 对于时间"14:30:45"，返回值为14*3600+30*60+45=52245秒
 */
uint32_t NFTimeUtility::GetDayAbsSec(time_t tTime)
{
#ifdef I18N_GLOBAL
    tTime += LOCAL_TIME_CORRECTION;
#endif
    tm* ptm = localtime(&tTime);
    return ptm->tm_hour * 3600 + ptm->tm_min * 60 + ptm->tm_sec;
}